﻿#region License

/*
    Copyright (c) 2012 Joseph Haluska
    This source is subject to the Microsoft Public License (Ms-PL).
    Please see http://www.microsoft.com/en-us/openness/licenses.aspx for details.
    All other rights reserved.
*/

#endregion License

using System;
using System.Collections.Generic;
using System.Text;

namespace NetExceptionReporter.Extensions
{
    public static class StringExtensions
    {
        #region Fields

        private static object _syncRoot = new object();
        private static List<int> _lineBuffer = new List<int>();
        private static string _buffer = string.Empty;

        #endregion Fields

        #region Methods

        public static string[] ToLines(this string str)
        {
            if (String.IsNullOrEmpty(str))
                return null;

            lock (_syncRoot)
            {
                _buffer = str;

                UpdateLineBuffer();

                string[] lines = new string[_lineBuffer.Count];

                for (int lineNumber = 0; lineNumber < _lineBuffer.Count; lineNumber++)
                    lines[lineNumber] = GetLine(lineNumber);

                return lines;
            }
        }

        public static string IndentLines(this string str, string buffer)
        {
            StringBuilder sb = new StringBuilder();

            foreach (string line in str.ToLines())
            {
                sb.Append(buffer);
                sb.Append(line);
            }

            return sb.ToString();
        }

        public static string TrimLines(this string str)
        {
            StringBuilder sb = new StringBuilder();

            foreach (string line in str.ToLines())
                sb.Append(line.Trim());

            return sb.ToString();
        }

        private static void UpdateLineBuffer()
        {
            // windows:     \r\n (remember order with the word "return" where 'r' is before 'n')
            // linux/unix:  \n
            // mac:         \n
            // classic mac: \r

            _lineBuffer.Clear();

            if (_buffer.Length > 0) // the buffer contains text
            {
                bool lineEndingFound = true; // true so this loop records position 0

                for (int i = 0; i < _buffer.Length; i++)
                {
                    if (lineEndingFound)
                    {
                        lineEndingFound = false;

                        _lineBuffer.Add(i);
                    }

                    //handles windows and classic mac
                    if (_buffer[i] == '\r')
                    {
                        if (_buffer[i + 1] == '\n')
                            i++;

                        lineEndingFound = true;
                    }

                    //handles unix and new mac
                    else if (_buffer[i] == '\n')
                    {
                        lineEndingFound = true;
                    }

                    // At this point we count on the loop to iterate to the next position which is either a letter or another \r and-or \n.
                    // The advantage is bounds checking is done at a minimum relying on the for loops condition check.
                }
            }
        }

        private static string GetLine(int lineNumber)
        {
            if (lineNumber < 0 || lineNumber >= _lineBuffer.Count)
                throw new ArgumentOutOfRangeException("lineNumber");

            int lineStart = _lineBuffer[lineNumber];

            if (lineNumber + 1 == _lineBuffer.Count)
                return _buffer.Substring(lineStart);

            else
            {
                int nextLineStart = _lineBuffer[lineNumber + 1];

                return _buffer.Substring(lineStart, nextLineStart - lineStart);
            }
        }

        #endregion Methods
    }
}